package org.me.marble;

import java.util.*;
import android.view.*;
import android.opengl.GLSurfaceView.*;

import android.os.Handler;
import java.io.IOException;
import android.content.Context;
import android.app.AlertDialog;
import org.xmlpull.v1.XmlPullParserException;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.egl.EGLConfig;

/**
*
* @author tungi
*/

public class GameRenderer implements Renderer{
    public Path path;
    public Camera cam;
    public Light light;
    public Marble marble;
    public MoveMarble marble_move;
    public ArrayList<AvailableObject> objects;

    private Timer timer;
    private Fog fog=null;
    private Shadow shadow;
    private BitmapText bt;
    private Pictures pics;
    private Handler handler;

    
    private float points=0;
    private long sys_time=0;
    private int delta_point=100,invert=1;
    private float _width,_height,time_left=-1;
    private boolean endlevel=false,pause=true;

    public GameRenderer(GameActivity game){
        Display display=((WindowManager)game.
                getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
        _width=display.getWidth();
        _height=display.getHeight();
        //create objects
        handler=new Handler();
        cam=new Camera();
        light=new Light();
        shadow=new Shadow(this);
        pics=new Pictures((int)_width,(int)_height);
        bt=new BitmapText((int)_width,(int)_height);

        path=new Path(gameObjects.Path.ordinal());
        marble=new Marble(gameObjects.Marble.ordinal());

        marble_move=new MoveMarble(marble,path);
        timer=new Timer(this,marble_move);

        objects=new ArrayList<AvailableObject>();
        try{//load current level
            LoadLevel load=new LoadLevel(OptionsLibrary.current_level);
            load.loadNextLevel(light,path,objects,fog=new Fog());
            
            marble_move.modifySpeed(OptionsLibrary.speedMult);
        }catch(XmlPullParserException xml_ppx){
            new AlertDialog.Builder(game)
                    .setTitle("Error!")
                    .setIcon(android.R.drawable.ic_dialog_alert)
                    .setMessage("Cannot load the current level!"+'\n'+
                        "Maybe reinstall solve the problem.")
                    .setPositiveButton("OK",game)
                    .show();
            game.finish();//if the loading broken e.g.: xml not well formed
        }catch(IOException iox){
            game.finish();//if something wrong through reading file
        }
        //if fog is enable
        if(!OptionsLibrary.fog)
            fog=null;
        //initialize objects after loading points from xml life
        path.init();
        path.loadTexture(GL10.GL_TEXTURE_2D);
        marble.init();
        marble.loadTexture(GL10.GL_TEXTURE_2D);
        for(int i=0;i<objects.size();++i){
            objects.get(i).init();
            objects.get(i).setNormal(path);
            objects.get(i).loadTexture(GL10.GL_TEXTURE_2D);
        }
        
        pics.loadTextures(fog!=null);

        GameActivity.game.levelInitialized();
    }

    public void onSurfaceCreated(GL10 gl,EGLConfig config){
        //initialize perspective view###########################################
        gl.glShadeModel(GL10.GL_SMOOTH);
        //define the color we want to be displayed as the "clipping wall"
        if(fog!=null)
            gl.glClearColor(fog.getColor()[0],fog.getColor()[1],
                    fog.getColor()[2],fog.getColor()[3]);
        else
            gl.glClearColor(0.0f,0.0f,0.0f,0.0f);
        gl.glMatrixMode(GL10.GL_PROJECTION);
        gl.glLoadIdentity();
        float size=0.01f*(float)Math.tan(Math.toRadians(45.0)/2);
        float ratio=_width/_height;
        //perspective:left,right,bottom,up,znear,zfar
        gl.glFrustumf(-size,size,-size/ratio,size/ratio,0.01f,100.0f);
        //orthographic:left,right,bottom,up,znear,zfar
        //gl.glOrthof(-1,1,-1/ratio,1/ratio,0.01f,100.0f);
        gl.glViewport(0,0,(int)_width,(int)_height);
        gl.glMatrixMode(GL10.GL_MODELVIEW);
        gl.glEnable(GL10.GL_DEPTH_TEST);

        //put textures to memory################################################
        OpenGLSystem.enable(gl,OpenGLSystem.TEXTURE);
        OpenGLSystem.enable(gl,OpenGLSystem.BLEND);
        DrawTexture.draw(gl,marble.getTexture());
        DrawTexture.draw(gl,path.getTexture());
        for(int i=0;i<objects.size();++i){
            Texture t=objects.get(i).getTexture();
            if(t!=null)
                DrawTexture.draw(gl,t);
        }
        DrawTexture.draw(gl,pics.life);
        DrawTexture.draw(gl,pics.pause);
        if(fog==null)
        	DrawTexture.draw(gl,pics.background);
        OpenGLSystem.disable(gl,OpenGLSystem.TEXTURE);
        OpenGLSystem.disable(gl,OpenGLSystem.BLEND);

        //remove the redundant bytebuffers from the memory
        path.getTexture().setData(null);
        marble.getTexture().setData(null);
        for(int i=0;i<objects.size();++i){
            Texture t=objects.get(i).getTexture();
            if(t!=null)
                objects.get(i).getTexture().setData(null);
        }

        //specifies the light###################################################
        light.specLight(gl);

        //init fog##############################################################
        if(fog!=null)
            fog.init(gl);
        
        sys_time=System.currentTimeMillis();
    }

    public void onSurfaceChanged(GL10 gl,int w,int h){
        _width=w;
        _height=h;
        gl.glViewport(0,0,w,h);
    }
    
    public void onDrawFrame(GL10 gl){
    	//get delta time
        float dt=System.currentTimeMillis()-sys_time;
        sys_time=System.currentTimeMillis();
        
        //search, which objects were picked up in the previous round###########
        AvailableObject ao;
        if(!pause){
            marble_move.move(gl,this,endlevel);
            for(int i=0;i<objects.size();++i){
                ao=objects.get(i);
                if(!endlevel){
                    int s=ao.pickedUp(marble_move.getPos());
                    if(s==availableObjects.pickedup.ordinal()){
                        ao.effect(this,marble_move);
                        if(ao.getID()!=gameObjects.Gate.ordinal())
                            objects.remove(i);
                        //play sound effect
                        if(OptionsLibrary.sound)
                        	SoundManager.playSound(ao.getID(),0,1.0f);
                    }
                    else if(s==availableObjects.missed.ordinal()){
                        
                    }
                }
            }
        }
        
        //draw shadow###########################################################
        if(OptionsLibrary.shadow && fog==null){
            shadow.drawShadow(gl);
            gl.glColor4f(1.0f,1.0f,1.0f,1.0f);
        }
        else{
        	//clear
            gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
            gl.glLoadIdentity();
            //##################################################################
            //enable light
            light.enableLight(gl);
            //set camera position
            cam.setLookPosition(gl,marble_move);
            //draw fog
            if(fog!=null)
                fog.draw(gl);
            //move everything down
            gl.glTranslatef(0.0f,-1.0f,0.0f);
            
            //calculate new positions###########################################            
            if(!pause){
                marble_move.move(gl,this,endlevel);
                for(int i=0;i<objects.size();++i){
                    ao=objects.get(i);
                    ao.update();                    
                }
            }
            //draw path#########################################################
            gl.glPushMatrix();
                gl.glTranslatef(0.0f,-marble.getRadius(),0.0f);
                path.draw(gl);
            gl.glPopMatrix();
            //draw available objects############################################    
            for(int i=0;i<objects.size();++i){
                gl.glPushMatrix();
                    ao=objects.get(i);
                    ao.draw(gl);
                gl.glPopMatrix();
            }
            //draw marble#######################################################
            gl.glPushMatrix();
                marble_move.transfom(gl);
                marble.draw(gl);
            gl.glPopMatrix();
        }

        //draw menu and background############################################## 
        //change graphics quality, to able draw menu
        String quality=new String(OptionsLibrary.graphics_quality);
        OptionsLibrary.graphics_quality="high";
        OpenGLSystem.enable(gl,OpenGLSystem.TEXTURE | OpenGLSystem.BLEND);

        //-->draw background
        if(fog==null)
        	pics.drawBackground(gl,marble_move.getDirection());        
        
        //-->draw lifes
        pics.drawLifes(gl,marble_move.getStrength());

        //-->draw pause text (image)
        if(pause)
            pics.drawPause(gl);
        
        float fps=MyMath.makeRound(1000f/dt,2);
        marble_move.modifyAngleDelta(dt);
        
        if(!pause && !endlevel && OptionsLibrary.game_type.equals("normal")){
            points+=((dt/delta_point)*OptionsLibrary.pointMult);
            time_left-=((float)(System.currentTimeMillis()-sys_time)/(float)1000);
            time_left=MyMath.makeRound(time_left,3);
        }
        else if(!pause && !endlevel && OptionsLibrary.game_type.equals("point")){   
        	
        }
        else if(!pause && !endlevel && OptionsLibrary.game_type.equals("time")){
        	points+=((float)(System.currentTimeMillis()-sys_time)/(float)1000);
        	time_left-=((float)(System.currentTimeMillis()-sys_time)/(float)1000);
            time_left=MyMath.makeRound(time_left,3);
        }
        
        //-->draw menu as bitmap
        String s=OptionsLibrary.game_type.equals("time")?"Time: ":"Points: ";
        bt.drawMenu(gl,"FPS: "+Float.toString(fps),
        		s+Integer.toString((int)MyMath.makeRound(points+OptionsLibrary.points,0)));
        
        if(time_left>=0){
            bt.drawTime(gl,Float.toString(time_left));
        }
        else if(!timer.isFired() && time_left<=0){
        	timer.fire();
        }
        
        //restore graphisc quality
        OpenGLSystem.disable(gl,OpenGLSystem.TEXTURE | OpenGLSystem.BLEND);
        OptionsLibrary.graphics_quality=quality;
    }

    public void addRunnable(int type,int delay){
        if(type==timerTypes.fast.ordinal())
            delta_point=20;
        else if(type==timerTypes.slow.ordinal())
            delta_point=300;
        else if(type==timerTypes.invert.ordinal())
            delta_point=50;
        
        if(!timer.isFired()){//launch the last added task immadiately
            timer.fire();
        }
        
        timer.setType(type);
        if(type!=timerTypes.endlevel.ordinal() && type!=timerTypes.fall.ordinal())
            time_left=(float)delay/1000f;
        else if(type==timerTypes.endlevel.ordinal() || type==timerTypes.fall.ordinal()){   
            handler.postDelayed(timer,delay);
            endlevel=true;
        }
    }

//######################################################################################
    
    public float getPoints(){
        return points;
    }
    
    public void invert(){
    	invert*=-1;
    }
    
    public boolean isEndLevel(){
    	return endlevel;
    }
    
    public boolean isPause(){
    	return pause;
    }    

    public void setDeltaPointToDefault(){
        delta_point=10;
    }

    public void setPause(boolean p){
        pause=p;
    }

    public void reset(){
    	timer.fire();
    	
    	pause=true;
    	endlevel=false;
    	marble_move.reset();
    	if(!OptionsLibrary.game_type.equals("time"))
        	points=0;
    }
    
    public void rotateDirection(double delta){
        //move the marble right or left
        marble_move.rotate(invert*delta);
    }
    
    public void exit(){
    	if(!timer.isFired())
    		timer.fire();
    }
}
